//COLORS
var Colors = {
  red: 0xf25346,
  white: 0xd8d0d1,
  brown: 0x59332e,
  brownDark: 0x23190f,
  pink: 0xF5986E,
  yellow: 0xf4ce93,
  blue: 0x68c3c0,

};

///////////////

// GAME VARIABLES
var game;
var deltaTime = 0;
var newTime = new Date().getTime();
var oldTime = new Date().getTime();
var ennemiesPool = [];
var particlesPool = [];
var particlesInUse = [];

function resetGame() {
  game = {
    speed: 0,
    initSpeed: .00035,
    baseSpeed: .00035,
    targetBaseSpeed: .00035,
    incrementSpeedByTime: .0000020,
    incrementSpeedByLevel: .000005,
    distanceForSpeedUpdate: 100,
    speedLastUpdate: 0,

    distance: 0,
    ratioSpeedDistance: 50,
    energy: 100,
    ratioSpeedEnergy: 3,

    level: 3,
    levelLastUpdate: 0,
    distanceForLevelUpdate: 1000,

    planeDefaultHeight: 100,
    // planeAmpHeight:80,
    planeAmpHeight: 50,
    // planeAmpWidth:75,
    planeAmpWidth: 100,
    planeMoveSensivity: 0.005,
    planeRotXSensivity: 0.0008,
    planeRotZSensivity: 0.0004,
    planeFallSpeed: .001,
    planeMinSpeed: 1.2,
    planeMaxSpeed: 1.6,
    planeSpeed: 0,
    planeCollisionDisplacementX: 0,
    planeCollisionSpeedX: 0,

    planeCollisionDisplacementY: 0,
    planeCollisionSpeedY: 0,

    seaRadius: 600,
    seaLength: 400,
    //seaRotationSpeed:0.006,
    wavesMinAmp: 5,
    wavesMaxAmp: 20,
    wavesMinSpeed: 0.001,
    wavesMaxSpeed: 0.003,

    cameraFarPos: 500,
    cameraNearPos: 150,
    cameraSensivity: 0.002,

    coinDistanceTolerance: 12,
    coinValue: 3,
    coinsSpeed: .5,
    coinLastSpawn: 0,
    distanceForCoinsSpawn: 100,
    coinNextSpawn: 70,

    ennemyDistanceTolerance: 10,
    ennemyValue: 10,
    ennemiesSpeed: .6,
    ennemyLastSpawn: 0,
    distanceForEnnemiesSpawn: 80,
    ennemySize: 6,
    ennemyYZD2: 100,

    status: "playing",
    pause: false,
  };
  fieldLevel.innerHTML = Math.floor(game.level);
  touchVirtualPos = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
}

//THREEJS RELATED VARIABLES

var scene,
  camera, aspectRatio, nearPlane, farPlane,
  renderer,
  container,
  controls;

//SCREEN & MOUSE VARIABLES

var HEIGHT = window.innerHeight, WIDTH = window.innerWidth,
  mousePos = { x: 0, y: 0 };
var fieldOfView = 30;
var nearPlane = .1;
farPlane = 10000;
var camPositionX = -300, camPositionY = 140, camPositionZ = 0;

var enableWidth, enableHeight;
function calEnableArea() {
  // camPositionY must be >= game.planeDefaultHeight
  var a = Math.abs(camPositionX);
  var b = Math.abs(camPositionY - game.planeDefaultHeight);
  var beta = Math.atan(b / a);
  var alpha = fieldOfView * Math.PI / 180;
  var enableHeight1 = a * Math.tan(alpha / 2 - beta);
  var enableHeight2 = a * Math.tan(alpha / 2 + beta);
  enableHeight = enableHeight1 + enableHeight2;
  var enableHeight2_2 = enableHeight2 - b;
  var hd1 = Math.sqrt(a * a + b * b);
  var hd2 = enableHeight2_2 * Math.sin(beta);
  var hd = hd1 + hd2;
  var enableWidthB = hd * Math.tan(alpha / 2) * 2 * (WIDTH / HEIGHT);
  var Mb = hd / Math.cos(alpha / 2);
  var Ma = a / Math.cos(alpha / 2 - beta);
  enableWidth = enableWidthB * (Ma / Mb);
  return enableWidth, enableHeight;
}

function randn_bm(min, max, skew) {
  let u = 0, v = 0;
  while (u === 0) u = Math.random(); //Converting [0,1) to (0,1)
  while (v === 0) v = Math.random();
  let num = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);

  num = num / 10.0 + 0.5; // Translate to 0 -> 1
  if (num > 1 || num < 0) num = randn_bm(min, max, skew); // resample between 0 and 1 if out of range
  num = Math.pow(num, skew); // Skew
  num *= max - min; // Stretch to fill range
  num += min; // offset to min
  return num;
}

//INIT THREE JS, SCREEN AND MOUSE EVENTS
function createScene() {

  HEIGHT = window.innerHeight;
  WIDTH = window.innerWidth;

  scene = new THREE.Scene();
  aspectRatio = WIDTH / HEIGHT;
  // fieldOfView = 48;
  // nearPlane = .1;
  // farPlane = 10000;
  camera = new THREE.PerspectiveCamera(
    fieldOfView,
    aspectRatio,
    nearPlane,
    farPlane
  );
  scene.fog = new THREE.Fog(0xf7d9aa, 100, 950);
  // camera.position.x = 0;
  camera.position.x = camPositionX;
  // camera.position.z = 200;
  camera.position.z = camPositionZ;
  camera.position.y = camPositionY;
  camera.lookAt(new THREE.Vector3(0, 100, 0));
  var test = camera.position.x;
  console.log()
  renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
  renderer.setSize(WIDTH, HEIGHT);

  renderer.shadowMap.enabled = true;

  container = document.getElementById('world');
  container.appendChild(renderer.domElement);

  window.addEventListener('resize', handleWindowResize, false);

  /*
  controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.minPolarAngle = -Math.PI / 2;
  controls.maxPolarAngle = Math.PI ;

  //controls.noZoom = true;
  //controls.noPan = true;
  //*/
}

// MOUSE AND SCREEN EVENTS

function handleWindowResize() {
  HEIGHT = window.innerHeight;
  WIDTH = window.innerWidth;
  calEnableArea();
  renderer.setSize(WIDTH, HEIGHT);
  camera.aspect = WIDTH / HEIGHT;
  camera.updateProjectionMatrix();
}

function handleMouseMove(event) {
  if (!game.pause) {
    var tx = -1 + (event.clientX / WIDTH) * 2;
    var ty = 1 - (event.clientY / HEIGHT) * 2;
    mousePos = { x: tx, y: ty };
  }
}

touchVirtualPos = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
touchLastVirtualPos = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
touchLastPos = { x: WIDTH / 2, y: HEIGHT / 2 };
function handleTouchStart(event) {
  if (!game.pause) {
    touchLastPos.x = event.touches[0].pageX;
    touchLastPos.y = event.touches[0].pageY;
    // console.log(touchVirtualPos);
    // console.log(touchLastPos);
  }
}

function handleTouchMove(event) {
  if (!game.pause) {
    event.preventDefault();
    // var tx = -1 + (event.touches[0].pageX / WIDTH) * 2;
    // var ty = 1 - (event.touches[0].pageY / HEIGHT) * 2;
    // mousePos = { x: tx, y: ty };
    // console.log(event.touches[0].pageX, event.touches[0].pageY, mousePos);
    if (WIDTH > HEIGHT) {
      var tmpVPosX = Math.max(0, Math.min(WIDTH, touchVirtualPos.x + (event.touches[0].pageX - touchLastPos.x) / 0.3));
      var tmpVPosY = Math.max(0, Math.min(HEIGHT, touchVirtualPos.y + (event.touches[0].pageY - touchLastPos.y) / 0.3));
      var tx = -1 + (tmpVPosX / WIDTH) * 2;
      var ty = 1 - (tmpVPosY / HEIGHT) * 2;
      mousePos = { x: tx, y: ty };
    }
    else {
      var tmpVPosX = Math.max(0, Math.min(WIDTH, touchVirtualPos.x + (event.touches[0].pageX - touchLastPos.x) / 0.3));
      var tmpVPosY = Math.max(0, Math.min(HEIGHT, touchVirtualPos.y + (event.touches[0].pageY - touchLastPos.y) / 0.3));
      var tx = -1 + (tmpVPosX / WIDTH) * 2;
      var ty = 1 - (tmpVPosY / HEIGHT) * 2;
      mousePos = { x: tx, y: ty };
    }
  }
}

function handleMouseUp(event) {
  if (!game.pause) {
    if (game.status == "waitingReplay") {
      resetGame();
      hideReplay();
    }
  }
}


function handleTouchEnd(event) {
  if (!game.pause) {
    touchVirtualPos.x = Math.max(0, Math.min(WIDTH, touchVirtualPos.x + (event.changedTouches[0].pageX - touchLastPos.x) / 0.3));
    touchVirtualPos.y = Math.max(0, Math.min(HEIGHT, touchVirtualPos.y + (event.changedTouches[0].pageY - touchLastPos.y) / 0.3));
    if (game.status == "waitingReplay") {
      resetGame();
      hideReplay();
    }
  }
  console.log(touchVirtualPos.x);
  console.log(touchVirtualPos.y);
}

// LIGHTS

var ambientLight, hemisphereLight, shadowLight;

function createLights() {

  hemisphereLight = new THREE.HemisphereLight(0xaaaaaa, 0x000000, .9)

  ambientLight = new THREE.AmbientLight(0xdc8874, .5);

  shadowLight = new THREE.DirectionalLight(0xffffff, .9);
  shadowLight.position.set(150, 350, 350);
  shadowLight.castShadow = true;
  shadowLight.shadow.camera.left = -400;
  shadowLight.shadow.camera.right = 400;
  shadowLight.shadow.camera.top = 400;
  shadowLight.shadow.camera.bottom = -400;
  shadowLight.shadow.camera.near = 1;
  shadowLight.shadow.camera.far = 1000;
  shadowLight.shadow.mapSize.width = 4096;
  shadowLight.shadow.mapSize.height = 4096;

  var ch = new THREE.CameraHelper(shadowLight.shadow.camera);

  //scene.add(ch);
  scene.add(hemisphereLight);
  scene.add(shadowLight);
  scene.add(ambientLight);

}


var Pilot = function () {
  this.mesh = new THREE.Object3D();
  this.mesh.name = "pilot";
  this.angleHairs = 0;

  var bodyGeom = new THREE.BoxGeometry(15, 15, 15);
  var bodyMat = new THREE.MeshPhongMaterial({ color: Colors.brown, shading: THREE.FlatShading });
  var body = new THREE.Mesh(bodyGeom, bodyMat);
  body.position.set(2, -12, 0);

  this.mesh.add(body);

  var faceGeom = new THREE.BoxGeometry(10, 10, 10);
  var faceMat = new THREE.MeshLambertMaterial({ color: Colors.pink });
  var face = new THREE.Mesh(faceGeom, faceMat);
  this.mesh.add(face);

  var hairGeom = new THREE.BoxGeometry(4, 4, 4);
  var hairMat = new THREE.MeshLambertMaterial({ color: Colors.brown });
  var hair = new THREE.Mesh(hairGeom, hairMat);
  hair.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, 2, 0));
  var hairs = new THREE.Object3D();

  this.hairsTop = new THREE.Object3D();

  for (var i = 0; i < 12; i++) {
    var h = hair.clone();
    var col = i % 3;
    var row = Math.floor(i / 3);
    var startPosZ = -4;
    var startPosX = -4;
    h.position.set(startPosX + row * 4, 0, startPosZ + col * 4);
    h.geometry.applyMatrix(new THREE.Matrix4().makeScale(1, 1, 1));
    this.hairsTop.add(h);
  }
  hairs.add(this.hairsTop);

  var hairSideGeom = new THREE.BoxGeometry(12, 4, 2);
  hairSideGeom.applyMatrix(new THREE.Matrix4().makeTranslation(-6, 0, 0));
  var hairSideR = new THREE.Mesh(hairSideGeom, hairMat);
  var hairSideL = hairSideR.clone();
  hairSideR.position.set(8, -2, 6);
  hairSideL.position.set(8, -2, -6);
  hairs.add(hairSideR);
  hairs.add(hairSideL);

  var hairBackGeom = new THREE.BoxGeometry(2, 8, 10);
  var hairBack = new THREE.Mesh(hairBackGeom, hairMat);
  hairBack.position.set(-1, -4, 0)
  hairs.add(hairBack);
  hairs.position.set(-5, 5, 0);

  this.mesh.add(hairs);

  var glassGeom = new THREE.BoxGeometry(5, 5, 5);
  var glassMat = new THREE.MeshLambertMaterial({ color: Colors.brown });
  var glassR = new THREE.Mesh(glassGeom, glassMat);
  glassR.position.set(6, 0, 3);
  var glassL = glassR.clone();
  glassL.position.z = -glassR.position.z

  var glassAGeom = new THREE.BoxGeometry(11, 1, 11);
  var glassA = new THREE.Mesh(glassAGeom, glassMat);
  this.mesh.add(glassR);
  this.mesh.add(glassL);
  this.mesh.add(glassA);

  var earGeom = new THREE.BoxGeometry(2, 3, 2);
  var earL = new THREE.Mesh(earGeom, faceMat);
  earL.position.set(0, 0, -6);
  var earR = earL.clone();
  earR.position.set(0, 0, 6);
  this.mesh.add(earL);
  this.mesh.add(earR);
}

Pilot.prototype.updateHairs = function () {
  //*
  var hairs = this.hairsTop.children;

  var l = hairs.length;
  for (var i = 0; i < l; i++) {
    var h = hairs[i];
    h.scale.y = .75 + Math.cos(this.angleHairs + i / 3) * .25;
  }
  this.angleHairs += game.speed * deltaTime * 40;
  //*/
}

var AirPlane = function () {
  this.mesh = new THREE.Object3D();
  this.mesh.name = "airPlane";

  // Cabin 机身

  var geomCabin = new THREE.BoxGeometry(80, 50, 50, 1, 1, 1);
  var matCabin = new THREE.MeshPhongMaterial({ color: Colors.red, shading: THREE.FlatShading });

  geomCabin.vertices[4].y -= 10;
  geomCabin.vertices[4].z += 20;
  geomCabin.vertices[5].y -= 10;
  geomCabin.vertices[5].z -= 20;
  geomCabin.vertices[6].y += 30;
  geomCabin.vertices[6].z += 20;
  geomCabin.vertices[7].y += 30;
  geomCabin.vertices[7].z -= 20;

  var cabin = new THREE.Mesh(geomCabin, matCabin);
  cabin.castShadow = true;
  cabin.receiveShadow = true;
  this.mesh.add(cabin);

  // Engine 前置白色引擎

  var geomEngine = new THREE.BoxGeometry(20, 50, 50, 1, 1, 1);
  var matEngine = new THREE.MeshPhongMaterial({ color: Colors.white, shading: THREE.FlatShading });
  var engine = new THREE.Mesh(geomEngine, matEngine);
  engine.position.x = 50;
  engine.castShadow = true;
  engine.receiveShadow = true;
  this.mesh.add(engine);

  // Tail Plane 竖直尾翼

  var geomTailPlane = new THREE.BoxGeometry(15, 20, 5, 1, 1, 1);
  var matTailPlane = new THREE.MeshPhongMaterial({ color: Colors.red, shading: THREE.FlatShading });
  var tailPlane = new THREE.Mesh(geomTailPlane, matTailPlane);
  tailPlane.position.set(-40, 20, 0);
  tailPlane.castShadow = true;
  tailPlane.receiveShadow = true;
  this.mesh.add(tailPlane);

  // Wings 机翼

  var geomSideWing = new THREE.BoxGeometry(30, 5, 120, 1, 1, 1);
  var matSideWing = new THREE.MeshPhongMaterial({ color: Colors.red, shading: THREE.FlatShading });
  var sideWing = new THREE.Mesh(geomSideWing, matSideWing);
  sideWing.position.set(0, 15, 0);
  sideWing.castShadow = true;
  sideWing.receiveShadow = true;
  this.mesh.add(sideWing);

  var geomWindshield = new THREE.BoxGeometry(3, 15, 20, 1, 1, 1);
  var matWindshield = new THREE.MeshPhongMaterial({ color: Colors.white, transparent: true, opacity: .3, shading: THREE.FlatShading });;
  var windshield = new THREE.Mesh(geomWindshield, matWindshield);
  windshield.position.set(5, 27, 0);

  windshield.castShadow = true;
  windshield.receiveShadow = true;

  this.mesh.add(windshield);

  var geomPropeller = new THREE.BoxGeometry(20, 10, 10, 1, 1, 1);
  geomPropeller.vertices[4].y -= 5;
  geomPropeller.vertices[4].z += 5;
  geomPropeller.vertices[5].y -= 5;
  geomPropeller.vertices[5].z -= 5;
  geomPropeller.vertices[6].y += 5;
  geomPropeller.vertices[6].z += 5;
  geomPropeller.vertices[7].y += 5;
  geomPropeller.vertices[7].z -= 5;
  var matPropeller = new THREE.MeshPhongMaterial({ color: Colors.brown, shading: THREE.FlatShading });
  this.propeller = new THREE.Mesh(geomPropeller, matPropeller);

  this.propeller.castShadow = true;
  this.propeller.receiveShadow = true;

  var geomBlade = new THREE.BoxGeometry(1, 80, 10, 1, 1, 1);
  var matBlade = new THREE.MeshPhongMaterial({ color: Colors.brownDark, shading: THREE.FlatShading });
  var blade1 = new THREE.Mesh(geomBlade, matBlade);
  blade1.position.set(8, 0, 0);

  blade1.castShadow = true;
  blade1.receiveShadow = true;

  var blade2 = blade1.clone();
  blade2.rotation.x = Math.PI / 2;

  blade2.castShadow = true;
  blade2.receiveShadow = true;

  this.propeller.add(blade1);
  this.propeller.add(blade2);
  this.propeller.position.set(60, 0, 0);
  this.mesh.add(this.propeller);

  // var wheelProtecGeom = new THREE.BoxGeometry(30,15,10,1,1,1);
  // var wheelProtecMat = new THREE.MeshPhongMaterial({color:Colors.red, shading:THREE.FlatShading});
  // var wheelProtecR = new THREE.Mesh(wheelProtecGeom,wheelProtecMat);
  // wheelProtecR.position.set(25,-20,25);
  // this.mesh.add(wheelProtecR);

  // var wheelTireGeom = new THREE.BoxGeometry(24,24,4);
  // var wheelTireMat = new THREE.MeshPhongMaterial({color:Colors.brownDark, shading:THREE.FlatShading});
  // var wheelTireR = new THREE.Mesh(wheelTireGeom,wheelTireMat);
  // wheelTireR.position.set(25,-28,25);

  // var wheelAxisGeom = new THREE.BoxGeometry(10,10,6);
  // var wheelAxisMat = new THREE.MeshPhongMaterial({color:Colors.brown, shading:THREE.FlatShading});
  // var wheelAxis = new THREE.Mesh(wheelAxisGeom,wheelAxisMat);
  // wheelTireR.add(wheelAxis);

  // this.mesh.add(wheelTireR);

  // var wheelProtecL = wheelProtecR.clone();
  // wheelProtecL.position.z = -wheelProtecR.position.z ;
  // this.mesh.add(wheelProtecL);

  // var wheelTireL = wheelTireR.clone();
  // wheelTireL.position.z = -wheelTireR.position.z;
  // this.mesh.add(wheelTireL);

  // var wheelTireB = wheelTireR.clone();
  // wheelTireB.scale.set(.5,.5,.5);
  // wheelTireB.position.set(-35,-5,0);
  // this.mesh.add(wheelTireB);

  var suspensionGeom = new THREE.BoxGeometry(4, 20, 4);
  suspensionGeom.applyMatrix(new THREE.Matrix4().makeTranslation(0, 10, 0))
  var suspensionMat = new THREE.MeshPhongMaterial({ color: Colors.red, shading: THREE.FlatShading });
  var suspension = new THREE.Mesh(suspensionGeom, suspensionMat);
  suspension.position.set(-35, -5, 0);
  suspension.rotation.z = -.3;
  this.mesh.add(suspension);

  this.pilot = new Pilot();
  this.pilot.mesh.position.set(-10, 27, 0);
  this.mesh.add(this.pilot.mesh);


  this.mesh.castShadow = true;
  this.mesh.receiveShadow = true;

};

Sky = function () {
  this.mesh = new THREE.Object3D();
  this.nClouds = 20;
  this.clouds = [];
  var stepAngle = Math.PI * 2 / this.nClouds;
  for (var i = 0; i < this.nClouds; i++) {
    var c = new Cloud();
    this.clouds.push(c);
    var a = stepAngle * i;
    var h = game.seaRadius + 200 + Math.random() * 200 + 50;
    c.mesh.position.y = Math.sin(a) * h;
    c.mesh.position.x = Math.cos(a) * h;
    c.mesh.position.z = (Math.random() - 0.5) * 800;
    c.mesh.rotation.z = a + Math.PI / 2;
    var s = 1 + Math.random() * 2;
    c.mesh.scale.set(s, s, s);
    this.mesh.add(c.mesh);
  }
}

Sky.prototype.moveClouds = function () {
  for (var i = 0; i < this.nClouds; i++) {
    var c = this.clouds[i];
    c.rotate();
  }
  this.mesh.rotation.z += game.speed * deltaTime;

}

Sea = function () {
  var geom = new THREE.CylinderGeometry(game.seaRadius, game.seaRadius, game.seaLength, 40, 10);
  geom.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
  geom.mergeVertices();
  var l = geom.vertices.length;

  this.waves = [];

  for (var i = 0; i < l; i++) {
    var v = geom.vertices[i];
    //v.y = Math.random()*30;
    this.waves.push({
      y: v.y,
      x: v.x,
      z: v.z,
      ang: Math.random() * Math.PI * 2,
      amp: game.wavesMinAmp + Math.random() * (game.wavesMaxAmp - game.wavesMinAmp),
      speed: game.wavesMinSpeed + Math.random() * (game.wavesMaxSpeed - game.wavesMinSpeed)
    });
  };
  var mat = new THREE.MeshPhongMaterial({
    color: Colors.blue,
    transparent: true,
    opacity: .8,
    shading: THREE.FlatShading,

  });

  this.mesh = new THREE.Mesh(geom, mat);
  this.mesh.name = "waves";
  this.mesh.receiveShadow = true;

}

Sea.prototype.moveWaves = function () {
  var verts = this.mesh.geometry.vertices;
  var l = verts.length;
  for (var i = 0; i < l; i++) {
    var v = verts[i];
    var vprops = this.waves[i];
    v.x = vprops.x + Math.cos(vprops.ang) * vprops.amp;
    v.y = vprops.y + Math.sin(vprops.ang) * vprops.amp;
    vprops.ang += vprops.speed * deltaTime;
    this.mesh.geometry.verticesNeedUpdate = true;
  }
}

Cloud = function () {
  this.mesh = new THREE.Object3D();
  this.mesh.name = "cloud";
  var geom = new THREE.CubeGeometry(20, 20, 20);
  var mat = new THREE.MeshPhongMaterial({
    color: Colors.white,

  });

  //*
  var nBlocs = 3 + Math.floor(Math.random() * 3);
  for (var i = 0; i < nBlocs; i++) {
    var m = new THREE.Mesh(geom.clone(), mat);
    m.position.x = i * 15;
    m.position.y = Math.random() * 10;
    m.position.z = Math.random() * 80;
    m.rotation.z = Math.random() * Math.PI * 2;
    m.rotation.y = Math.random() * Math.PI * 2;
    var s = .1 + Math.random() * .9;
    m.scale.set(s, s, s);
    this.mesh.add(m);
    m.castShadow = true;
    m.receiveShadow = true;

  }
  //*/
}

Cloud.prototype.rotate = function () {
  var l = this.mesh.children.length;
  for (var i = 0; i < l; i++) {
    var m = this.mesh.children[i];
    m.rotation.z += Math.random() * .005 * (i + 1);
    m.rotation.y += Math.random() * .002 * (i + 1);
  }
}

// 障碍物
Ennemy = function () {
  var geom = new THREE.TetrahedronGeometry(game.ennemySize, 2);
  var mat = new THREE.MeshPhongMaterial({
    color: Colors.red,
    shininess: 0,
    specular: 0xffffff,
    shading: THREE.FlatShading
  });
  this.mesh = new THREE.Mesh(geom, mat);
  this.mesh.castShadow = true;
  this.angle = 0;
  this.dist = 0;
}

EnnemiesHolder = function () {
  this.mesh = new THREE.Object3D();
  this.ennemiesInUse = [];
}

// 产生障碍物
EnnemiesHolder.prototype.spawnEnnemies = function () {
  var nEnnemies = game.level;
  // var mEnnemies = nEnnemies * 3 / 4;
  for (var i = 0; i < nEnnemies; i++) {
    var ennemy;
    if (ennemiesPool.length) {
      ennemy = ennemiesPool.pop();
    } else {
      ennemy = new Ennemy();
    }

    ennemy.angle = - (i * 0.1);
    // ennemy.distance = game.seaRadius + game.planeDefaultHeight + (-1 + Math.random() * 2) * (game.planeAmpHeight - 20);
    ennemy.distance = game.seaRadius + airplane.mesh.position.y + (-1 + Math.random() * 2) * (game.planeAmpHeight - 20);
    // game.planeDefaultHeight - game.planeAmpHeight
    ennemy.mesh.position.y = -game.seaRadius + Math.sin(ennemy.angle) * ennemy.distance;
    ennemy.mesh.position.x = Math.cos(ennemy.angle) * ennemy.distance;
    if (i % 2 != 0) {
      ennemy.mesh.position.z = airplane.mesh.position.z + 100 * (Math.random() - 0.5);
    }
    else {
      ennemy.mesh.position.z = Math.random() * enableWidth * 0.83 - enableWidth * 0.83 / 2;
    }
    this.mesh.add(ennemy.mesh);
    this.ennemiesInUse.push(ennemy);
  }
}

EnnemiesHolder.prototype.rotateEnnemies = function () {
  for (var i = 0; i < this.ennemiesInUse.length; i++) {
    var ennemy = this.ennemiesInUse[i];
    ennemy.angle += game.speed * deltaTime * game.ennemiesSpeed;

    if (ennemy.angle > Math.PI * 2) ennemy.angle -= Math.PI * 2;

    ennemy.mesh.position.y = -game.seaRadius + Math.sin(ennemy.angle) * ennemy.distance;
    ennemy.mesh.position.x = Math.cos(ennemy.angle) * ennemy.distance;
    ennemy.mesh.rotation.z += Math.random() * .1;
    ennemy.mesh.rotation.y += Math.random() * .1;

    //var globalEnnemyPosition =  ennemy.mesh.localToWorld(new THREE.Vector3());
    var tmpAirplanePost = airplane.mesh.position.clone();
    tmpAirplanePost.x = tmpAirplanePost.x + 10;
    // console.log(tmpAirplanePost);
    var diffPos = tmpAirplanePost.sub(ennemy.mesh.position.clone());
    var d = diffPos.length();
    // if (d<game.ennemyDistanceTolerance){  // 碰撞
    if ((diffPos.y * diffPos.y + diffPos.z * diffPos.z) < game.ennemyYZD2 && Math.abs(diffPos.x) < 30) {  // 碰撞，视为圆柱
      particlesHolder.spawnParticles(ennemy.mesh.position.clone(), 15, Colors.red, 3); // !!!
      // console.log(tmpAirplanePost);
      // console.log(ennemy.mesh.position);
      // console.log(diffPos);
      ennemiesPool.unshift(this.ennemiesInUse.splice(i, 1)[0]);
      this.mesh.remove(ennemy.mesh);
      game.planeCollisionSpeedX = 100 * diffPos.z / d;
      game.planeCollisionSpeedY = 100 * diffPos.y / d;
      ambientLight.intensity = 2;

      // removeEnergy();  // !!!
      i--;
    } else if (ennemy.angle > Math.PI) {
      ennemiesPool.unshift(this.ennemiesInUse.splice(i, 1)[0]);
      this.mesh.remove(ennemy.mesh);
      i--;
    }
  }
}

Particle = function () {
  var geom = new THREE.TetrahedronGeometry(3, 0);
  var mat = new THREE.MeshPhongMaterial({
    color: 0x009999,
    shininess: 0,
    specular: 0xffffff,
    shading: THREE.FlatShading
  });
  this.mesh = new THREE.Mesh(geom, mat);
}

Particle.prototype.explode = function (pos, color, scale) {
  var _this = this;
  var _p = this.mesh.parent;
  this.mesh.material.color = new THREE.Color(color);
  this.mesh.material.needsUpdate = true;
  this.mesh.scale.set(scale, scale, scale);
  var targetX = pos.x + (-1 + Math.random() * 2) * 50;
  var targetY = pos.y + (-1 + Math.random() * 2) * 50;
  var targetZ = pos.z + (-1 + Math.random() * 2) * 50;
  var speed = .6 + Math.random() * .2;
  TweenMax.to(this.mesh.rotation, speed, { x: Math.random() * 12, y: Math.random() * 12, z: Math.random() * 12 });
  TweenMax.to(this.mesh.scale, speed, { x: .1, y: .1, z: .1 });
  TweenMax.to(this.mesh.position, speed, {
    x: targetX, y: targetY, z: targetZ, delay: Math.random() * .1, ease: Power2.easeOut, onComplete: function () {
      if (_p) _p.remove(_this.mesh);
      _this.mesh.scale.set(1, 1, 1);
      particlesPool.unshift(_this);
    }
  });
}

ParticlesHolder = function () {
  this.mesh = new THREE.Object3D();
  this.particlesInUse = [];
}

ParticlesHolder.prototype.spawnParticles = function (pos, density, color, scale) {

  var nPArticles = density;
  for (var i = 0; i < nPArticles; i++) {
    var particle;
    if (particlesPool.length) {
      particle = particlesPool.pop();
    } else {
      particle = new Particle();
    }
    this.mesh.add(particle.mesh);
    particle.mesh.visible = true;
    var _this = this;
    particle.mesh.position.z = pos.z;
    particle.mesh.position.y = pos.y;
    particle.mesh.position.x = pos.x;
    particle.explode(pos, color, scale);
  }
}

Coin = function (color) {
  var geom = new THREE.TetrahedronGeometry(2, 0);
  var mat = new THREE.MeshPhongMaterial({
    // color: 0x009999,
    color: color,
    shininess: 0,
    specular: 0xffffff,

    shading: THREE.FlatShading
  });
  this.mesh = new THREE.Mesh(geom, mat);
  this.mesh.castShadow = true;
  this.angle = 0;
  this.dist = 0;
}

CoinsHolder = function (nCoins) {
  this.mesh = new THREE.Object3D();
  this.coinsInUse = [];
  this.coinsPool = [];
  for (var i = 0; i < nCoins; i++) {
    var coin = new Coin(0x009999);
    this.coinsPool.push(coin);
  }
}

CoinsHolder.prototype.spawnCoins = function () {

  var nCoins = 4 + Math.floor(Math.random() * 8);
  var d = game.seaRadius + game.planeDefaultHeight + (-1 + Math.random() * 2) * (game.planeAmpHeight - 20);
  var amplitude = 10 + Math.round(Math.random() * 10);
  var posZ = (Math.random() - 0.5) * enableWidth * 0.7;
  var diffPosZ = (Math.random() - 0.5) * enableWidth / 10;
  while (Math.abs(posZ + diffPosZ * nCoins) > enableWidth * 0.4) {
    posZ = (Math.random() - 0.5) * enableWidth * 0.6;
    diffPosZ = (Math.random() - 0.5) * enableWidth / 10;
  }
  nCoins = 10;
  var radius = (Math.random() * 5) + 6
  for (var i = 0; i < nCoins; i++) {
    var coin;
    if (this.coinsPool.length) {
      coin = this.coinsPool.pop();
    } else {
      coin = new Coin(0x009999);
    }
    this.mesh.add(coin.mesh);
    this.coinsInUse.push(coin);
    // coin.angle = - (i * 0.02);
    coin.angle = 0;
    coin.theta = 2 * Math.PI / nCoins * i;
    coin.radius = radius;
    // coin.distance = d + Math.cos(i * .5) * amplitude;
    coin.distance = d + amplitude;
    coin.mesh.position.y = -game.seaRadius + Math.sin(coin.angle) * coin.distance + Math.cos(coin.theta) * radius;
    coin.mesh.position.x = Math.cos(coin.angle) * coin.distance;
    // posZ = posZ + diffPosZ;
    coin.mesh.position.z = posZ + Math.sin(coin.theta) * radius;
    // console.log(coin.mesh.position);
  }
}

CoinsHolder.prototype.rotateCoins = function () {
  for (var i = 0; i < this.coinsInUse.length; i++) {
    var coin = this.coinsInUse[i];
    if (coin.exploding) continue;
    coin.angle += game.speed * deltaTime * game.coinsSpeed;
    if (coin.angle > Math.PI * 2) coin.angle -= Math.PI * 2;
    coin.mesh.position.y = -game.seaRadius + Math.sin(coin.angle) * coin.distance + Math.cos(coin.theta) * coin.radius;
    coin.mesh.position.x = Math.cos(coin.angle) * coin.distance;
    coin.mesh.rotation.z += Math.random() * .1;
    coin.mesh.rotation.y += Math.random() * .1;

    //var globalCoinPosition =  coin.mesh.localToWorld(new THREE.Vector3());
    var diffPos = airplane.mesh.position.clone().sub(coin.mesh.position.clone());
    var d = diffPos.length();
    if (d < game.coinDistanceTolerance) {
      this.coinsPool.unshift(this.coinsInUse.splice(i, 1)[0]);
      this.mesh.remove(coin.mesh);
      particlesHolder.spawnParticles(coin.mesh.position.clone(), 5, 0x009999, .8);
      addEnergy();
      i--;
    } else if (coin.angle > Math.PI) {
      this.coinsPool.unshift(this.coinsInUse.splice(i, 1)[0]);
      this.mesh.remove(coin.mesh);
      i--;
    }
  }
}


// 3D Models
var sea;
var airplane;

function createPlane() {
  airplane = new AirPlane();
  airplane.mesh.scale.set(.25, .25, .25);
  airplane.mesh.position.y = game.planeDefaultHeight;
  scene.add(airplane.mesh);
}

function createSea() {
  sea = new Sea();
  sea.mesh.position.y = -game.seaRadius;
  scene.add(sea.mesh);
}

function createSky() {
  sky = new Sky();
  sky.mesh.position.y = -game.seaRadius;
  scene.add(sky.mesh);
}

function createCoins() {

  coinsHolder = new CoinsHolder(20);
  scene.add(coinsHolder.mesh)
}

function createEnnemies() {
  for (var i = 0; i < 10; i++) {
    var ennemy = new Ennemy();
    ennemiesPool.push(ennemy);
  }
  ennemiesHolder = new EnnemiesHolder();
  //ennemiesHolder.mesh.position.y = -game.seaRadius;
  scene.add(ennemiesHolder.mesh)
}

function createParticles() {
  for (var i = 0; i < 10; i++) {
    var particle = new Particle();
    particlesPool.push(particle);
  }
  particlesHolder = new ParticlesHolder();
  //ennemiesHolder.mesh.position.y = -game.seaRadius;
  scene.add(particlesHolder.mesh)
}

function loop() {
  if (!game.pause) {
    newTime = new Date().getTime();
    deltaTime = newTime - oldTime;
    oldTime = newTime;

    if (game.status == "playing") {

      // 根据距离产生燃料
      // if (Math.floor(game.distance) % game.distanceForCoinsSpawn == 0 && Math.floor(game.distance) > game.coinLastSpawn) {
      //   game.coinLastSpawn = Math.floor(game.distance);
      //   coinsHolder.spawnCoins();
      // }
      if (Math.floor(game.distance) >= game.coinNextSpawn && Math.floor(game.distance) > game.coinLastSpawn) {
        game.coinLastSpawn = Math.floor(game.distance);
        game.coinNextSpawn = (Math.random() + 1) / 2 * game.distanceForCoinsSpawn + game.coinNextSpawn;
        coinsHolder.spawnCoins();
      }
      // 加速
      if (Math.floor(game.distance) % game.distanceForSpeedUpdate == 0 && Math.floor(game.distance) > game.speedLastUpdate) {
        game.speedLastUpdate = Math.floor(game.distance);
        game.targetBaseSpeed += game.incrementSpeedByTime * deltaTime;
      }


      if (Math.floor(game.distance) % game.distanceForEnnemiesSpawn == 0 && Math.floor(game.distance) > game.ennemyLastSpawn) {
        game.ennemyLastSpawn = Math.floor(game.distance);
        ennemiesHolder.spawnEnnemies(); // !!!
      }

      if (Math.floor(game.distance) % game.distanceForLevelUpdate == 0 && Math.floor(game.distance) > game.levelLastUpdate) {
        game.levelLastUpdate = Math.floor(game.distance);
        game.level++;
        fieldLevel.innerHTML = Math.floor(game.level);
        // 升级后会降一次速
        game.targetBaseSpeed = game.initSpeed + game.incrementSpeedByLevel * game.level
      }


      updatePlane();
      updateDistance();
      updateEnergy();
      if (game.targetBaseSpeed < game.baseSpeed) {
        game.baseSpeed += (game.targetBaseSpeed - game.baseSpeed) * deltaTime * 0.005;
      } else {
        game.baseSpeed += (game.targetBaseSpeed - game.baseSpeed) * deltaTime * 0.02;
      }
      game.speed = game.baseSpeed * game.planeSpeed;

    } else if (game.status == "gameover") {
      game.speed *= .99;
      airplane.mesh.rotation.z += (-Math.PI / 2 - airplane.mesh.rotation.z) * .0002 * deltaTime;
      airplane.mesh.rotation.x += 0.0003 * deltaTime;
      game.planeFallSpeed *= 1.05;
      airplane.mesh.position.y -= game.planeFallSpeed * deltaTime;

      if (airplane.mesh.position.y < -200) {
        showReplay();
        game.status = "waitingReplay";

      }
    } else if (game.status == "waitingReplay") {

    }


    airplane.propeller.rotation.x += .2 + game.planeSpeed * deltaTime * .005;
    sea.mesh.rotation.z += game.speed * deltaTime;//*game.seaRotationSpeed;

    if (sea.mesh.rotation.z > 2 * Math.PI) sea.mesh.rotation.z -= 2 * Math.PI;

    ambientLight.intensity += (.5 - ambientLight.intensity) * deltaTime * 0.005;

    coinsHolder.rotateCoins(); // !!!
    ennemiesHolder.rotateEnnemies(); // !!!

    sky.moveClouds();
    sea.moveWaves();

    renderer.render(scene, camera);
    requestAnimationFrame(loop);
  }
}

function updateDistance() {
  game.distance += game.speed * deltaTime * game.ratioSpeedDistance;
  fieldDistance.innerHTML = Math.floor(game.distance);
  var d = 502 * (1 - (game.distance % game.distanceForLevelUpdate) / game.distanceForLevelUpdate);
  levelCircle.setAttribute("stroke-dashoffset", d);

}

var blinkEnergy = false;

function updateEnergy() {
  // game.energy -= game.speed*deltaTime*game.ratioSpeedEnergy;
  game.energy = Math.max(0, game.energy);
  energyBar.style.right = (100 - game.energy) + "%";
  energyBar.style.backgroundColor = (game.energy < 50) ? "#f25346" : "#68c3c0";

  if (game.energy < 30) {
    energyBar.style.animationName = "blinking";
  } else {
    energyBar.style.animationName = "none";
  }

  if (game.energy < 1) {
    game.status = "gameover";
  }
}

function addEnergy() {
  game.energy += game.coinValue;
  game.energy = Math.min(game.energy, 100);
}

function removeEnergy() {
  game.energy -= game.ennemyValue;
  game.energy = Math.max(0, game.energy);
}



function updatePlane() {

  game.planeSpeed = normalize(mousePos.x, -.5, .5, game.planeMinSpeed, game.planeMaxSpeed);
  var targetY = normalize(mousePos.y, -1, 1, game.planeDefaultHeight - game.planeAmpHeight, game.planeDefaultHeight + game.planeAmpHeight);
  var targetX = normalize(mousePos.x, -1, 1, -0.4 * enableWidth, 0.4 * enableWidth);
  // var targetX = normalize(mousePos.x, -1, 1, -game.planeAmpWidth, game.planeAmpWidth);
  // console.log(targetX, targetY);
  game.planeCollisionDisplacementX += game.planeCollisionSpeedX;
  targetX += game.planeCollisionDisplacementX;


  game.planeCollisionDisplacementY += game.planeCollisionSpeedY;
  targetY += game.planeCollisionDisplacementY;

  airplane.mesh.position.y += (targetY - airplane.mesh.position.y) * deltaTime * game.planeMoveSensivity;
  airplane.mesh.position.z += (targetX - airplane.mesh.position.z) * deltaTime * game.planeMoveSensivity;
  airplane.mesh.rotation.z = (targetY - airplane.mesh.position.y) * 0.025;  // 上下(y)移动，旋转 z 轴
  airplane.mesh.rotation.x = (targetX - airplane.mesh.position.z) * 0.02;   // 左右(z)移动，旋转 x 轴
  // airplane.mesh.position.y += (targetY-airplane.mesh.position.y)*deltaTime*game.planeMoveSensivity;
  // airplane.mesh.position.x += (targetX-airplane.mesh.position.x)*deltaTime*game.planeMoveSensivity;
  // airplane.mesh.rotation.z = (targetY-airplane.mesh.position.y)*deltaTime*game.planeRotXSensivity;
  // airplane.mesh.rotation.x = (airplane.mesh.position.y-targetY)*deltaTime*game.planeRotZSensivity;

  // var targetCameraZ = normalize(game.planeSpeed, game.planeMinSpeed, game.planeMaxSpeed, game.cameraNearPos, game.cameraFarPos);
  // camera.fov = normalize(mousePos.x,-1,1,40, 80);
  // camera.updateProjectionMatrix ()
  // camera.position.y += (airplane.mesh.position.y - camera.position.y)*deltaTime*game.cameraSensivity;

  game.planeCollisionSpeedX += (0 - game.planeCollisionSpeedX) * deltaTime * 0.03;
  game.planeCollisionDisplacementX += (0 - game.planeCollisionDisplacementX) * deltaTime * 0.01;
  game.planeCollisionSpeedY += (0 - game.planeCollisionSpeedY) * deltaTime * 0.03;
  game.planeCollisionDisplacementY += (0 - game.planeCollisionDisplacementY) * deltaTime * 0.01;

  airplane.pilot.updateHairs();
}

function showReplay() {
  replayMessage.style.display = "block";
}

function hideReplay() {
  replayMessage.style.display = "none";
}

function normalize(v, vmin, vmax, tmin, tmax) {
  var nv = Math.max(Math.min(v, vmax), vmin);
  var dv = vmax - vmin;
  var pc = (nv - vmin) / dv;
  var dt = tmax - tmin;
  var tv = tmin + (pc * dt);
  return tv;
}

var fieldDistance, energyBar, replayMessage, fieldLevel, levelCircle;

function init(event) {

  // UI

  fieldDistance = document.getElementById("distValue");
  energyBar = document.getElementById("energyBar");
  replayMessage = document.getElementById("replayMessage");
  fieldLevel = document.getElementById("levelValue");
  levelCircle = document.getElementById("levelCircleStroke");

  resetGame();
  calEnableArea();
  createScene();

  createLights();
  createPlane();
  createSea();
  createSky();
  createCoins();
  createEnnemies();
  createParticles();

  document.addEventListener('mousemove', handleMouseMove, false);
  // document.addEventListener('touchmove', handleTouchMove, false);
  document.addEventListener('touchstart', handleTouchStart, { passive: false });
  document.addEventListener('touchmove', handleTouchMove, { passive: false });
  document.addEventListener('mouseup', handleMouseUp, false);
  document.addEventListener('touchend', handleTouchEnd, false);
  document.getElementById("preloader01").style.display = "none";//隐藏
  // document.getElementById("preloader01").style.display="block";//显示
  //   if (window.DeviceOrientationEvent) {
  //     window.addEventListener('deviceorientation', function (event) {
  //       var alpha = event.alpha;//绕Z轴旋转时，水平旋转时，数值为0度到360度。 
  //       var beta = event.beta;//绕X轴旋转时，手机横向旋转时，数值为-180度到180度 
  //       var gamma = event.gamma;//绕Y轴旋转时，手机竖向旋转时，数值为-90度到90度 
  //       //手机不同，旋转数值会有差异 
  //       document.getElementById('alpha').innerHTML = alpha;
  //       document.getElementById('beta').innerHTML = beta;
  //       document.getElementById('gamma').innerHTML = gamma;
  //     }, false);
  // } else {
  //   document.getElementById('alpha').innerHTML = '你的浏览器不支持陀螺仪';
  // }
  // var element = document.documentElement;
  // if (element.requestFullscreen) {
  //   element.requestFullscreen();
  // } else if (element.mozRequestFullScreen) {
  //   element.mozRequestFullScreen();
  // } else if (element.webkitRequestFullscreen) {
  //   element.webkitRequestFullscreen();
  // } else if (element.msRequestFullscreen) {
  //   element.msRequestFullscreen();
  // }

  var btn_elems = document.querySelectorAll('.fixed-action-btn');
  var btn_instances = M.FloatingActionButton.init(btn_elems);
  M.toast({ html: '这是一个飞行小游戏', classes: 'rounded demo-link' }, 100);
  setTimeout(() => {
    M.Toast.dismissAll();
    setTimeout(() => {
      M.toast({ html: '移动鼠标或触控来操作飞机', classes: 'rounded demo-link' }, 100)
    }, 400);
  }, 3000);
  setTimeout(() => {
    M.Toast.dismissAll();
    setTimeout(() => {
      M.toast({ html: '躲避红色障碍物', classes: 'rounded demo-link' }, 100)
    }, 400);
  }, 6000);
  setTimeout(() => {
    M.Toast.dismissAll();
    setTimeout(() => {
      M.toast({ html: '捕获蓝色奖励以获得加分', classes: 'rounded demo-link' }, 100)
    }, 400);
  }, 9000);
  setTimeout(() => {
    M.Toast.dismissAll();
    setTimeout(() => {
      M.toast({ html: '可以随时点击右下角的菜单', classes: 'rounded demo-link' }, 100)
    }, 400);
  }, 3000 * 4);
  setTimeout(() => {
    M.Toast.dismissAll();
    setTimeout(() => {
      M.toast({ html: '享受你的游戏旅程吧', classes: 'rounded demo-link' }, 100)
    }, 400);
  }, 3000 * 5);
  // setTimeout(() => M.toast({ html: '躲避红色障碍物', classes: 'rounded' }, 10), 8000);
  // setTimeout(() => M.toast({ html: '尝试通过移动鼠标或触控来操作飞机', classes: 'rounded' }, 10), 12000);
  var elems = document.querySelectorAll('.modal');
  var instances = M.Modal.init(elems, options = {
    // opacity: 0.1,
    onOpenStart: function () {
      game.pause = true;
      console.log(touchVirtualPos.x);
      console.log(touchVirtualPos.y);
      console.log(mousePos);
    },
    onCloseEnd: function () {
      newTime = new Date().getTime();
      oldTime = new Date().getTime();
      mousePos.x = -1 + (touchVirtualPos.x / WIDTH) * 2;
      mousePos.y = 1 - (touchVirtualPos.y / HEIGHT) * 2;
      console.log(touchVirtualPos.x);
      console.log(touchVirtualPos.y);
      console.log(mousePos);
      game.pause = false;
      console.log(touchVirtualPos.x);
      console.log(touchVirtualPos.y);
      console.log(mousePos);
      loop();
    },
  });

  console.log(instances);
  // instances[0].open();
  // console.log(instances[0].isOpen);
  // while(!instances[0].isOpen){
  //   console.log(instances[0].isOpen);
  var elems = document.querySelectorAll('.collapsible');
  var instances = M.Collapsible.init(elems, options);
  loop();
  // }
}

function pauseGame() {
  game.pause = true;
}

window.addEventListener('load', init, false);
